sequenceDiagram
autonumber
actor Twin as Virtual Twin (Worker Agent)
participant SimClock as Simulator Clock (Timeline Engine)
participant SimKafka as Simulator Kafka (Topic: agent-intents)
participant SimCons as Simulator Consumer
participant Qwen as AI Simulator Service (Qwen 2.5)
participant GoSvc as Core Go Service (Route: /inject)
participant MainKafka as Corporate Kafka (Topic: receipt-clean-products-topic)
Note over Twin: Virtual Time Step: T
Twin->>Twin: 1. Evaluate Behavior (Markov Chain Action: SUBMIT_REPORT)
Twin->>Twin: 2. Calculate Clock Skew (Weibull Delay = 140 min)
Twin->>SimKafka: 3. Buffer Intent Metadata (Target Time: T + 140)
Note over SimKafka: Event queued cleanly within<br/>isolated simulation boundaries
Note over SimClock: Virtual time moves rapidly forward...<br/>Timeline reaches execution boundary: T + 140 min
SimClock->>SimCons: Trigger Consumer Execution
SimCons->>SimKafka: 4. Fetch Matured Intent Payload
SimKafka-->>SimCons: Return Intent Context (worker_id, crop_name, experience_class)
SimCons->>Qwen: 5. Request Transaction Generation (JSON Prompt)
Note over Qwen: Evaluates experience_class profile.<br/>If JUNIOR: injects structural flaws (e.g., zero weight).<br/>If SENIOR: builds pristine dataset.
Qwen-->>SimCons: Return Domain JSON Structure
Note over SimCons: Payload compiled successfully
SimCons->>GoSvc: 6. HTTP POST /api/v1/sim-control/inject?topic=receipt-clean-products-topic&session_id=family-uuid-777-abc
GoSvc->>MainKafka: 7. Emit Pristine/Defective Payload to Corporate Bus [6й package grpc]
MainKafka-->>GoSvc: Record Delivery Ack [6й package grpc]
GoSvc-->>SimCons: HTTP 200 OK {"status":"EMITTED"}
Note over Twin: Asynchronous stochastic trajectory fulfilled
BA: Бизнес-процессы AgroTracker
В открытом доступе представлена демонстрационная версия метода. В настоящей публичной документации отображены не все шаги, технические сценарии и приватные эндпоинты для системы цифровых симуляторов бизнес-процессов.
- Полная спецификация метода: Будет доступна только во внутреннем контуре разработки (Confluence / Swagger Enterprise).
1 1. Автоматическое распознавание полевых медиаданных (OCR и Voice v2)
Процесс регламентирует бесшовную оцифровку фотографий качества урожая с полей (PyTorch) и голосовых отчетов наемных рабочих (Whisper) через распределенный gRPC v2 контур.
1.1 Пошаговый алгоритм выполнения ИИ-обработки
- Шаг 1 (Интерфейс): Наемный рабочий записывает аудио-комментарий или фотографирует собранный урожай. Приложение запускает
gRPC Client Streamingна бэкенд Go. - Шаг 2 и 3 (Go Оркестратор): Сервер на лету агрегирует бинарные чанки в
bytes.Bufferи асинхронно загружает через методPutObjectв S3-бакетreceiptsхранилища MinIO. - Шаг 4 и 5 (Контур ИИ): Go-сервис инициирует высокопроизводительный вызов gRPC v2 инференса. Применяется паттерн передачи по ссылке (By-Reference): бэкенд не пересылает тяжелые байты файлов, а передает моделям Whisper (
RecognizeSpeech) и Llama Vision (CheckCropHealth) строго текстовыйs3_key. - Шаг 5 (Валидация Qwen): Языковая модель
Qwen 2.5:3bпроизводит разбор текста и проверяет инвариант достаточности физических параметров (обязательное наличие связки Название + Вес в граммах + Дискретные единицы). - Аварийный выход (Очередь модерации): Если рабочий допустил ошибку или пропустил вес (например, сказал “Собрал два ведра” без указания массы), транзакция признается дефектной, прерывается и перенаправляется в СУБД в таблицу
voice_moderation_queueсо статусомpendingдля ручного исправления оператором.
2 2. Процесс пакетного снабжения телеметрии и геофенсинга
Процесс регламентирует мгновенный прием пакетных массивов оффлайн-координат перемещения сотрудников, разгружая реляционную базу данных холдинга.
3 Бизнес-правила и ограничения (Важно для аналитиков)
• Запрет отрицательного баланса: Ни один эндпоинт или фоновый ИИ-воркер не имеет права переводить поле weight_grams (или portions) в таблицах инвентаря в значение меньше нуля. Любая транзакция, нарушающая это условие, атомарно откатывается СУБД, возвращая ошибку RowsAffected == 0. • Правило закрытия трассы (Case ID): Идентификатор смены session_id (или item_id продукта) считается открытым и доступным для сквозного майнинга процессов в PM4Py до тех пор, пока совокупный статус сессии равен IN_PROGRESS, а вес собираемого сырья в контуре больше нуля. Записи со статусом CLOSED, CORRUPTED или REJECTED переводятся ETL-алгоритмами в категорию архивного холодного лога (DWH ClickHouse), закрывая трассу для текущего операционного мониторинга.
4 Завершение и закрытие рабочей смены
Автоматический заслон против фрода времени: Если рабочий намеренно перевел часы назад, чтобы обмануть систему и искусственно раздуть рабочее время, шлюз Go поймает нестыковку хронологии, мгновенно заблокирует транзакцию, переведет сессию в статус CORRUPTED и передаст инцидент службе безопасности для штрафных санкций.
5 Приложение
5.1 Процесс №2: Симулятор наемного полевого труда (Цифровые двойники)
Этот процесс описывает автономную генерацию транзакционных намерений (интентов) ИИ-персонами работников холдинга, синхронизированную с распределением Вейбулла и семантическим ИИ-генератором рапортов.
5.1.1 Доменная структура Агро-Твина
Вместо шкал голода и состава холодильника, Событийная модель Процесса №2 оперирует производственными характеристиками: 1. StaticProfile (Профиль сотрудника холдинга): • worker_id — реальный Integer-ключ сотрудника из таблицы user_field_permissions. • experience_class — квалификация (JUNIOR (склонен к браку), MIDDLE, SENIOR (работает быстрее)). • laziness_factor — коэффициент лени (влияет на плотность выбора активности IDLE). • assigned_fields — массив разрешенных полей ([assigned_field_id, secondary_field_id]), полученный круговым сдвигом из СУБД. 2. DynamicState (Производственные шкалы смены): • current_fatigue — уровень физической усталости от 0.0 до 1.0 (растет с каждым часом работы, сбрасывается в CLOSED). • session_status — OFF_DUTY (смена закрыта), IN_PROGRESS (работает на поле). • active_session_uuid — UUID текущей открытой сессии в Go-бэкенде. • time_on_field — количество часов, отработанных в текущую смену.
5.1.2 Матрица переключения состояний Маркова (Агро-Марков)
На каждом часовом тике, продвигаемом Движком, модель Агро-Твина пересчитывает веса рулетки для выбора следующего действия: START_SHIFT (Начать смену), IDLE (Отдых/Перекур), FIELD_WORK (Выполнение разрешенной активности), SUBMIT_REPORT (Голосовой/Фото отчет), CLOSE_SHIFT (Завершить день). Модификаторы весов: • Усталость (fatigue > 0.80): Вероятность продуктивной работы FIELD_WORK штрафуется на -0.50, вероятность ухода на перекур IDLE или закрытия смены CLOSE_SHIFT возрастает на +0.30. • Длительность смены (time_on_field >= 8 часов): Вес действия CLOSE_SHIFT принудительно увеличивается на +0.60, заставляя твина завершить рабочий день.
5.1.3 Инвариант Будущего Времени (Когда и Какое событие)
После того как Марковская цепь выбрала действие, Движок Вейбулла определяет временную координату виртуального будущего — через сколько минут это событие материализуется.
\[ \text{Virtual Delay (Minutes)} = \beta \cdot \left(-\ln(1 - l)\right)^{\frac{1}{\alpha}} \cdot 60 \]
- Какое событие: Твин выбрал интент SUBMIT_REPORT (наговорить аудио-рапорт).
- Когда произойдет: Формула Вейбулла выдала задержку в 140 минут. Если сейчас на часах симулятора 10:00, то в топик agent-intents летит намерение совершить оцифровку в виртуальном будущем: 10:00 + 140 мин = 12:20.
- Замыкание цикла: Планировщик симулятора прокручивает время. В 12:20 Движок извлекает этот интент и пинает внутреннюю ручку публикации, отправляя сырые данные в ИИ-сервис.
5.1.4 Системный промпт ИИ-генератора рапортов (Генерация Payload)
Когда наступает время выполнения интента SUBMIT_REPORT, Симулятор не дергает Go-бэкенд напрямую, а обращается к генеративной LLM (Ollama/Qwen), чтобы она сформировала реалистичный “человеческий” рапорт для тестирования нашего ИИ-конвейера.
действие: SIMULATE_WORKER_TRANSACTION_JSON
Ты — ИИ-генератор транзакционных данных для симулятора наемного труда агрохолдинга.
Твоя задача — принять параметры текущего стохастического состояния цифрового двойника и вернуть СТРОГО валидный JSON-пакет, соответствующий доменной схеме закупки, готовки или сбора урожая, без какого-либо лишнего текста, markdown-разметки или объяснений.
Входной контекст стохастического события:
- Текущее действие: {{.action_type}} (Принимает: PURCHASE, COOKING, CONSUMPTION, FIELD_WORK)
- Выращиваемая культура на поле: {{.crop_name}} (например: STRAWBERRY, APPLE)
- Квалификация твина: {{.experience_class}} (Принимает: JUNIOR, MIDDLE, SENIOR)
- Класс дохода / Лимиты холдинга: {{.income_class_or_limit}} (например: LOW, HIGH)
ЖЕСТКИЕ ПРАВИЛА ГЕНЕРАЦИИ ВЕЛИЧИН (Влияние параметров):
1. Если действие = PURCHASE (Закупка сырья/инвентаря): Сгенерируй массив "items". В "units" укажи количество упаковок, в "weight_grams" — чистый вес. Запрещено указывать абстрактные "порции" в чеках. Итоговая "total_price" должна зависеть от лимита (HIGH — дорогие товары, LOW — базовые).
2. Если действие = COOKING (Трансформация вещества): Спиши сырье из остатков по UUID и сгенерируй новое готовое многопорционное блюдо на основе культуры (например, "Клубничный джем" для STRAWBERRY), выставив флаг "is_cooked_dish": true.
3. Влияние Квалификации (experience_class):
- Если SENIOR: Расчетный вес продукции "weight_grams" стабильный и высокий, в JSON отсутствуют аномалии.
- Если JUNIOR: Сделай стохастическую ошибку в JSON (например, занули поле веса "weight_grams" при наличии порций или выдай невалидную категорию), чтобы протестировать отработку инварианта достаточности шкал на Go-бэкенде и отправку в Окно брака.
Верни ответ строго в структуре целевого доменного DTO:
{
"group_id": "family-uuid-777",
"correlation_id": "TX-SIM-{{.timestamp}}",
"data": {
"receipt_no": "SIM-BILL-XXXX",
"total_price": 0.0,
"items": [
{
"item_id": "uuid-ингредиента-или-товара",
"name": "Название продукта на русском языке",
"category": "Категория",
"portions": 1.0,
"weight_grams": 500.0,
"price": 0.0
}
]
}
}
5.1.5 Как этот промпт закрывает двустороннюю связь?
- Движок симулятора считает стохастику Маркова и задержку Вейбулла, определяя: «Работник JUNIOR на поле STRAWBERRY совершил сбор урожая в 14:00» [model.qmd].
- Он подставляет эти параметры в промпт и шлет в Ollama/Qwen.
- Так как работник JUNIOR, модель Qwen генерирует «дефектный» JSON, где в массиве items указаны порции, но weight_grams: 0.0.
- Симулятор забирает этот готовый JSON и шлет его в ручку /inject нашего Go-сервиса.
- Наш Go-сервер принимает пакет, прогоняет через бизнес-логику валидации шкал, видит нулевой вес при наличии порций, блокирует автозачисление и честно отправляет транзакцию в voice_moderation_queue (Окно брака), фиксируя инцидент модерации в СУБД.
5.2 Примеры
{
"group_id": "family-uuid-777-abc",
"correlation_id": "TX-SIM-178291029400",
"data": {
"receipt_no": "SIM-HARVEST-AURORA-01",
"total_price": 0.0,
"items": [
{
"item_id": "a1b2-c3d4-e5f6-7777",
"name": "Клубника сортовая (Свежий сбор)",
"category": "Ягоды",
"portions": 1.0,
"weight_grams": 25000.0,
"price": 0.0
}
]
}
}
{
"group_id": "family-uuid-777-abc",
"correlation_id": "TX-SIM-178291029555",
"data": {
"receipt_no": "SIM-HARVEST-ERROR-99",
"total_price": 0.0,
"items": [
{
"item_id": "f8e7-d6c5-b4a3-8888",
"name": "Яблоки Симиренко (Сбор в ящики)",
"category": "Фрукты",
"portions": 3.0,
"weight_grams": 0.0,
"price": 0.0
}
]
}
}
{
"group_id": "family-uuid-999-xyz",
"correlation_id": "TX-SIM-178291029600",
"data": {
"receipt_no": "SIM-BILL-PRO-STORE",
"total_price": 14500.0,
"items": [
{
"item_id": "tool-4432-bb88",
"name": "Секатор Садовый Профессиональный (Япония)",
"category": "Инвентарь",
"portions": 2.0,
"weight_grams": 700.0,
"price": 7250.0
}
]
}
}
6 Приложение 2
6.1 Сквозной чек-лист интеграционного тестирования (E2E Тест-план)
Данный регламент предназначен для инженеров автоматизации тестирования холдинга для верификации мигрированного Go-бэкенда.
Блок 1: Контур сессий и иерархия доступов
• TC-001: Инициализация смены легитимным сотрудником холдинга. • Шаг: Взять тестовый user_id = 15624510 из первой группы СУБД. Отправить POST /api/v1/sessions/tracking с field_id = 2 (теплица, на которую у него есть явные права в user_field_permissions). • Ожидаемый результат: HTTP 201 Created. В таблице tracking_sessions создается строка в статусе IN_PROGRESS, а в tracking_event_log атомарно падает запись START_SESSION с типом данных JSONB. • TC-002: Попытка взлома иерархии доступа. • Шаг: Отправить запрос на старт сессии от имени user_id = 15624510, но передать field_id = 12 (поле чужого агрохолдинга, к которому у сотрудника нет прав). • Ожидаемый результат: HTTP 403 Forbidden, бизнес-логика Go-сервиса блокирует транзакцию, возвращая структурированное доменное исключение. Блок 2: Высоконагруженный шлюз пакетной телеметрии (React Native оффлайн-кэш)
• TC-003: Пакетный сброс координат (SLA шлюза < 5мс). • Шаг: Сгенерировать пакет BatchTelemetryRequest, содержащий массив из 100 географических точек (TelemetryRequest). Отправить POST /api/v1/sessions/tracking/{uuid}/telemetry/batch. • Ожидаемый результат: HTTP 200 OK, processed_points: 100. В СУБД Postgres запросов не происходит (полное отсутствие логов в БД). В Kafka топик telemetry.batch.v1 мгновенно записывается JSON-срез. • TC-004: Устойчивость клиента при аварии шины данных (Fail-Safe Сценарий). • Шаг: Имитировать падение брокера (выполнить docker-compose stop kafka), отправить валидный пакет телеметрии. • Ожидаемый результат: HTTP 503 Service Unavailable. Клиент franz-kafka фиксирует сбой доставки, HTTP-транспорт перехватывает ошибку и дает сигнал мобильному приложению React Native принудительно удерживать точки в локальном кэше телефона до следующей связи. Блок 3: Хронологические инварианты закрытия смен
• TC-005: Корректный расчет длительности смены.
• Шаг: Отправить PATCH /api/v1/sessions/tracking/{session_id} со статусом CLOSED.
• Ожидаемый результат: HTTP 200 OK. В Postgres обновляются поля end_time и total_duration_seconds (разница времени старта и текущего времени вычисляется корректно). В Kafka транслируется событие закрытия в топик telemetry.events.v1.
• TC-006: Перехват аномалии часов телефона (Time Corruption Barrier).
• Шаг: Имитировать сбой системного времени на устройстве (перевести время назад). Отправить PATCH-запрос, в котором текущее время сервера оказывается меньше времени старта сессии (currentTime < session.StartedAt).
• Ожидаемый результат: HTTP 422 Unprocessable Entity. Транзакция блокируется, репозиторий принудительно переводит сессию в аварийный статус CORRUPTED, а событие эвакуируется в специальный топик инцидентов ИБ для разбора службой безопасности.
::: {.cell}
sequenceDiagram
autonumber
participant Client as Мобильный Клиент / Симулятор
participant Nginx as Nginx Proxy (nginx.conf)
participant MW as AuthMiddleware (middleware.go)
participant Hnd as TrackerHandler (router.go)
participant Pub as KafkaPublisher (publisher.go)
participant Broker as Apache Kafka Broker
Client->>Nginx: HTTP POST /api/v1/sessions/tracking/{id}/telemetry/batch (JSON + JWT)
note over Nginx: Валидация размера тела (max 50M)<br/>Включение Gzip декомпрессии
Nginx->>MW: Проксирование потока байт
note over MW: Zero-Overhead парсинг JWT Claims<br/>Проверка срока жизни exp
alt Токен просрочен или поврежден
MW-->>Client: HTTP 401 Unauthorized (GOV_AUTH_DENIED)
else Токен валиден
note over MW: Наполнение контекста выполнения:<br/>context.WithValue("user_id", claims.UserID)
MW->>Hnd: Передача управления (ServeHTTP)
end
Hnd->>Hnd: PathValue("session_id") + json.NewDecoder()
note over Hnd: Компоновка транзитного пакета kafkaPayload
Hnd->>Pub: PublishEvent(ctx, "telemetry.batch.v1", sessionID, payloadBytes)
Pub->>Broker: ProduceSync() (Сжатие Gzip + Партиционирование по key=sessionID)
alt Брокер Кафки временно недоступен
Broker-->>Hnd: Ошибка сети
Hnd-->>Client: HTTP 503 Service Unavailable (Смартфон удерживает кэш локально)
else Публикация успешна
Broker-->>Hnd: Record Ack
Hnd-->>Client: HTTP 200 OK (SUCCESS: Пакет принят в очередь)
end
:::